#include "encoding.hh"

using namespace std;

   unsigned char* hf_;
   pthread_mutex_t work_mutex;

void* Encoder::thread_handler(void* param) {
         /* parse parameters */
     Encoder* obj = ((param_encoder*)param)->obj;
     free(param);
     char* filename;
     filename=(char*)malloc(sizeof(char)*10);
     printf("thread_hander:%d\n",obj->n_);   
     int total=0;
     int buffertotal=0;

     while(true) {  
      fileBlock_Item_t temp;
      blockCipertext_Item_t input;
      obj->fileblockbuffer_->Extract(&temp);
      int left;

      if(temp.type==FILE_HEAD){
       left=temp.file_header.left;
       int filenameSize=temp.file_header.filenameSize;
       memcpy(filename,temp.file_header.data,filenameSize);
       input.type=temp.type;
       memcpy(&input.file_header, &temp.file_header, sizeof(fileHead_t));
       obj->addBlockCipertext(input);
      printf("it has add a header into encoding %d\n",input.file_header.filesize);
      }

      if(temp.type==BLOCKCP || temp.type==BLOCKCP_END) {
       // int length1=strlen((char*)temp.block.data);
     // printf("-------------%s\n",temp.block.data);
      int blocksize=temp.block.datasize;
      input.blockcipertext.data=(unsigned char*)malloc(sizeof(unsigned char)*blocksize);
      unsigned char* blockbuffer=(unsigned char*)malloc(sizeof(unsigned char)*blocksize);
      unsigned char* blockcpbuffer=(unsigned char*)malloc(sizeof(unsigned char)*blocksize);
      memcpy(blockbuffer,temp.block.data,blocksize);
      obj->cryptoObj_->generateHash(blockbuffer,blocksize,temp.block.key);
      obj->cryptoObj_->encryptWithKey(blockbuffer,blocksize,temp.block.key,blockcpbuffer);
      obj->cryptoObj_->generateHash(blockcpbuffer,blocksize,input.blockcipertext.token);
      memcpy(input.blockcipertext.key,temp.block.key,32);
      memcpy(input.blockcipertext.data,blockcpbuffer,blocksize);
      memcpy(obj->hfbuffer_+total,input.blockcipertext.token,32);
    //  printf("encoding::============the cipertxt is %s\n",input.blockcipertext.data);
      total+=32;
      input.blockcipertext.cipertextSize=blocksize;
      input.blockcipertext.cipertextid=temp.block.blockid;
      //int length3=strlen((char*)blockcpbuffer);
      //int length2=strlen((char*)input.blockcipertext.data);
      input.type=temp.type;
      obj->addBlockCipertext(input);
      //printf("encoding----------blockdata size: ,cpsize:,blockcpsize:\n");
      if(temp.type==BLOCKCP_END){
    	  blockCipertext_Item_t input1;
    	  obj->cryptoObj_->generateHash(obj->hfbuffer_,total,hf_);
    	  input1.type=FILE_HF;
	  memcpy(input1.hfObj.hf,hf_,32);
	  obj->addBlockCipertext(input1);
    	  //storage the file into leveldb
    	  obj->storageHf(hf_,filename);
    	  printf("the hr is!!----------%d %s\n",total,hf_);
        }
       free( input.blockcipertext.data);
       free(blockbuffer);
       free(blockcpbuffer);
      }
  }    
    free(filename);
     return NULL;
}
void* Encoder::collect(void* param) {
 Encoder *obj=(Encoder*)param;

 while(true) {
   blockCipertext_Item_t output;
   obj->blockcipertextbuffer_->Extract(&output);

   Uploader::Item_t input;

   if(output.type==FILE_HEAD){
	   input.type=output.type;
	   input.fileObj.filesize=output.file_header.filesize;
            printf("encoding:the file size is %d\n",input.fileObj.filesize);
	   input.fileObj.left=output.file_header.left;
       input.fileObj.numOfblock=output.file_header.numOfblock;
       obj->uploaderObj_->add(input,sizeof(input));
   }
   else if(output.type==BLOCKCP ||output.type==BLOCKCP_END) {
   memcpy(input.blockcpObj.blockcp_header.fingerFP,output.blockcipertext.token,32);
   input.blockcpObj.blockcp_header.blockcpid= output.blockcipertext.cipertextid;
   memcpy( input.blockcpObj.blockcp_header.key,output.blockcipertext.key,32);
   int blockSize=output.blockcipertext.cipertextSize;
   input.blockcpObj.blockcp_header.blockcpSize=blockSize;
   input.type=output.type;
   input.blockcpObj.data=(unsigned char*)malloc(sizeof(unsigned char)*blockSize);
   memcpy(input.blockcpObj.data,output.blockcipertext.data,blockSize);
   //printf("encoding::collect==============the cipertext is %s\n",input.blockcpObj.data);
   obj->uploaderObj_->add(input,sizeof(input));
   printf("this is collect----%d,the key is %s\n",output.type,input.blockcpObj.blockcp_header.key);

   free(input.blockcpObj.data);
  }
   else if(output.type==FILE_HF){
	   memcpy(input.hfObj.hf,output.hfObj.hf,32);
	   input.type=output.type;
	   obj->uploaderObj_->add(input,sizeof(input));
    printf("encoding the hf is :%d\n",input.type);
   }
  }
 }
 
 Encoder::Encoder( int n, int m, int r,Uploader* uploaderObj) {
 n_=n;
 m_=m;
 r_=r;
  pthread_mutex_init(&work_mutex,NULL);
// fileblockbuffer_=(RingBuffer<FileBlock_Item_t>*)malloc(sizeof(RingBuffer<FileBlock_Item_t>));
//blockcipertext_=(RingBuffer<BlockCipertext_Item_t>*)malloc(sizeof(RingBuffer<BlockCipertext_Item_t>));
 fileblockbuffer_=new RingBuffer<fileBlock_Item_t>(RB_SIZE,true,1);
 blockcipertextbuffer_=new RingBuffer<blockCipertext_Item_t>(RB_SIZE,true,1);
 cryptoObj_=new CryptoPrimitive(HIGH_SEC_PAIR_TYPE);
 uploaderObj_=uploaderObj;
 hf_=(unsigned char*)malloc(sizeof(unsigned char)*32);
 hfbuffer_=(unsigned char*)malloc(sizeof(unsigned char)*32*n_);
 param_encoder* temp = (param_encoder*)malloc(sizeof(param_encoder));
 temp->index = 1;
 temp->obj = this;

 //create/open the key-value database
  //create/open the kyey-value database
  dbOptions_.create_if_missing=true;
  //modify the sst file
  dbOptions_.write_buffer_size=MEM_TABLE_SIZE;
  //modify the cache ???
  dbOptions_.block_cache=leveldb::NewLRUCache(BLOCK_CACHE_SIZE);
  dbOptions_.filter_policy=leveldb::NewBloomFilterPolicy(BLOOM_FILTER_KEY_BITS);
  leveldb::Status openStat=leveldb::DB::Open(dbOptions_,"./meta/DedupDB",&db_);
   if(openStat.ok()==false){
    fprintf(stderr,"Error:fail to open/create the database %s \n",dbDirName_.c_str());
    fprintf(stderr, "Status: %s \n", openStat.ToString().c_str());
		exit(1);
  }

 pthread_create(&tid_[0],0,&thread_handler,(void *)temp);
 
 pthread_create(&tid_[1],0,&collect,(void *)this);

}


Encoder::~Encoder() {
  delete db_;
  delete dbOptions_.block_cache;
  delete dbOptions_.filter_policy;
  delete fileblockbuffer_;
  delete blockcipertextbuffer_;
  delete cryptoObj_;
  free(hfbuffer_);
  free(hf_);
}
int Encoder::storageHf(unsigned char* hfbuffer,char* filename){
          printf("the filename is %s\n",filename);
	 leveldb::Slice *valueSlice,*keySlice;
	 keySlice=new leveldb::Slice(filename,strlen(filename));
	 valueSlice=new leveldb::Slice((char*)hfbuffer,32);
	 batch_.Clear();
	 batch_.Put(*keySlice,*valueSlice);
	            leveldb::Status writeStat=db_->Write(writeOptions_,&batch_);
	            if(writeStat.ok()==false) {
	                fprintf(stderr, "Error: fail to perform batched writes!\n");
		        fprintf(stderr, "Status: %s \n", writeStat.ToString().c_str());

		          /*release the mutex lock DBLock_*/
	                 printf("write to leveldb is wrong\n");
	              }else
	                 printf("write to leveldb is correct %s %s\n",filename,hfbuffer);
   delete valueSlice;
   delete keySlice;
}
void Encoder::indicateEnd(){
   pthread_join(tid_[1],NULL);
}

int Encoder::addFileblock(fileBlock_Item_t item) {

 if(item.type==BLOCKCP || item.type==BLOCKCP_END){
   Encoder::fileBlock_Item_t input;
   int chunklength=item.block.datasize;
   input.block.data=(unsigned char*)malloc(sizeof(unsigned char)*chunklength);
   memcpy(input.block.data,item.block.data,chunklength);
   input.block.blockid=item.block.blockid;
   input.block.datasize=chunklength;
   input.type=item.type; 
  // int length=strlen((char*)input.block.data);
    fileblockbuffer_->Insert(&input,sizeof(input));
  printf("encoding:it has added one\n");
  free(input.block.data);
 }else{
   printf("encoding:it has added one %d\n",item.type);
   fileblockbuffer_->Insert(&item,sizeof(item));
  }
  
  return 0;
}

int Encoder::addBlockCipertext(blockCipertext_Item_t item) {
   blockcipertextbuffer_->Insert(&item,sizeof(item));
    // printf("has added one blockcipertext\n");
  return 0;
}

